(function(angular) {
  'use strict';
  angular
    .module('fs.format')
    .directive('fsNumFormat', [
      '$parse',
      'formatService',
      'storageService',
      'keyDownService',
      'SYS_PARAMS',
      function($parse, formatService, storageService, keyDownService, SYS_PARAMS) {
        return {
          restrict: 'A',
          require: 'ngModel',
          link: function($scope, $element, $attr, ngModelCtrl) {
            linkFunction($scope, $element, $attr, ngModelCtrl, 'number', $parse, formatService, storageService, keyDownService, SYS_PARAMS);
          }
        };
      }
    ])
    .directive('fsMoneyFormat', [
      '$parse',
      'formatService',
      'storageService',
      'keyDownService',
      'SYS_PARAMS',
      function($parse, formatService, storageService, keyDownService, SYS_PARAMS) {
        return {
          restrict: 'A',
          require: 'ngModel',
          link: function($scope, $element, $attr, ngModelCtrl) {
            linkFunction($scope, $element, $attr, ngModelCtrl, 'money', $parse, formatService, storageService, keyDownService, SYS_PARAMS);
          }
        };
      }
    ])
    .directive('fsPctFormat', [
      '$parse',
      'formatService',
      'storageService',
      'keyDownService',
      'SYS_PARAMS',
      function($parse, formatService, storageService, keyDownService, SYS_PARAMS) {
        return {
          restrict: 'A',
          require: 'ngModel',
          link: function($scope, $element, $attr, ngModelCtrl) {
            linkFunction($scope, $element, $attr, ngModelCtrl, 'percentage', $parse, formatService, storageService, keyDownService, SYS_PARAMS);
          }
        };
      }
    ])
    .directive('fsSstFormat', [
      '$filter',
      '$parse',
      'formatService',
      'storageService',
      'keyDownService',
      'SYS_PARAMS',
      function($filter, $parse, formatService, storageService, keyDownService, SYS_PARAMS) {
        return {
          restrict: 'A',
          require: 'ngModel',
          link: function($scope, $element, $attr, ngModelCtrl) {
            linkFunction($scope, $element, $attr, ngModelCtrl, 'sensitive', $parse, formatService, storageService, keyDownService, SYS_PARAMS);
          }
        };
      }
    ])
    .directive('fsDateFormat', [
      '$parse',
      'formatService',
      'storageService',
      'keyDownService',
      'SYS_PARAMS',
      function($parse, formatService, storageService, keyDownService, SYS_PARAMS) {
        return {
          restrict: 'A',
          require: 'ngModel',
          link: function($scope, $element, $attr, ngModelCtrl) {
            linkFunction($scope, $element, $attr, ngModelCtrl, 'date', $parse, formatService, storageService, keyDownService, SYS_PARAMS);
          }
        };
      }
    ])
    .directive('fsUppercase', [
      '$filter',
      function($filter) {
        return {
          restrict: 'A',
          require: 'ngModel',
          link: function($scope, $element, $attr, ngModelCtrl) {
            if ($scope.$eval($attr.fsUppercase) === true || $filter('uppercase')($attr.fsUppercase) == 'TRUE') {
              ngModelCtrl.$parsers.push(function(input) {
                var capitalized = angular.isString(input) ? $filter('uppercase')(input) : input;
                if (capitalized !== input) {
                  // see where the cursor is before the update so that we can set it back
                  var selection = getCursortPosition($element[0]);
                  ngModelCtrl.$setViewValue(capitalized);
                  ngModelCtrl.$render();
                  // set back the cursor after rendering
                  $element[0].selectionStart = selection;
                  $element[0].selectionEnd = selection;
                }
                return capitalized;
              });
            }
          }
        };
      }
    ]);

  function linkFunction($scope, $element, $attr, ngModelCtrl, formatType, $parse, formatService, storageService, keyDownService, SYS_PARAMS) {
    var pattern,
      getter,
      setter,
      value,
      watcher,
      watchers = [],
      needFormat = true,
      formComponentCtrl = $scope.$parent.formComponentCtrl;

    var isEmpty = function(value) {
      return value === null || angular.isUndefined(value) || (angular.isString(value) ? value.trim() == '' : false);
    };

    var getPattern = function(formatType) {
      var _pattern, fsFormat;

      if (formatType == 'number') {
        fsFormat = $attr.fsNumFormat;
      } else if (formatType == 'money') {
        fsFormat = $attr.fsMoneyFormat;
      } else if (formatType == 'percentage') {
        fsFormat = $attr.fsPctFormat;
      } else if (formatType == 'date') {
        fsFormat = $attr.fsDateFormat;
      } else if (formatType == 'sensitive') {
        fsFormat = $attr.fsSstFormat;
      }
      if (fsFormat && (formatType == 'number' || formatType == 'money' || formatType == 'percentage') && fsFormat.match('[#0]')) {
        _pattern = fsFormat;
      } else if (formatType == 'date' && fsFormat.indexOf('yyyy') != -1) {
        _pattern = fsFormat;
      } else {
        // for numberFormat the _pattern may be 'vm.ratingType.scoreDecimal' or '#0.0' or 'defaultInteger' etc.
        // for moneyFormat the pattern may be 'USD' or 'vm.product.loanAmtRangeCcy' or 'loanAmtRangeCcy' or 'moeny0' etc
        if (!fsFormat) {
          return null;
        }
        _pattern = $parse(fsFormat)(formComponentCtrl.fsGridConfig ? formComponentCtrl.fsGridConfig.appScope : formComponentCtrl);
        if (isEmpty(_pattern)) _pattern = $parse(fsFormat)(formComponentCtrl.ngModel);
        //for sensitive
        if (isEmpty(_pattern)) _pattern = $parse(fsFormat)(formComponentCtrl);
      }
      return _pattern || fsFormat;
    };
    $scope.$on('$destroy', function() {
      while (watchers.length) {
        watchers.shift()();
      }
    });

    getter = $parse($attr.ngModel);
    setter = getter.assign;

    watcher = $scope.$watch(
      $attr.ngModel,
      function(value, oldValue) {
        if (needFormat && !isEmpty(value)) {
          if (angular.isNumber(value) && value == 0) {
            if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
              ngModelCtrl.$viewValue = '';
              $element.val('');
              return;
            }
          }
          if (formatType == 'number') {
            pattern = getPattern(formatType);
            value = formatService.formatNumber(value, pattern);
            setter($scope, formatService.parseNumber(value));
            $element.val(value);
          } else if (formatType == 'money') {
            pattern = getPattern(formatType);
            value = formatService.formatMoney(value, pattern);
            setter($scope, formatService.parseMoney(value));
            $element.val(value);
          } else if (formatType == 'percentage') {
            pattern = getPattern(formatType);
            value = formatService.formatPercentage(value, pattern);
            setter($scope, formatService.parsePercentage(value));
            $element.val(value);
          } else if (formatType == 'date') {
            pattern = getPattern(formatType);
            value = formatService.formatDate(value, pattern);
            $element.val(value);
          } else if (formatType == 'sensitive') {
            pattern = getPattern(formatType);
            value = formatService.formatSst(value, pattern);
            $element.val(value);
          }
        }
      },
      true
    );
    watchers.push(watcher);
    try {
      if (formatType == 'number') {
        //--for format like 'vm.feeCode.feeCcy'
        watcher = $scope.fsGridConfig.appScope.$watch($attr.fsNumFormat, function(pattern) {
          if (needFormat && !isEmpty(pattern)) {
            value = getter($scope);
            if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
              if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
                $element.val('');
                return;
              }
            }
            if (!isEmpty(value)) {
              value = formatService.formatNumber(value, pattern);
              setter($scope, formatService.parseNumber(value));
              $element.val(value);
            }
          }
        });
        watchers.push(watcher);
        watcher = $scope.$watch('ngModel.' + $attr.fsNumFormat, function(pattern) {
          if (needFormat && !isEmpty(pattern)) {
            value = getter($scope);
            if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
              if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
                $element.val('');
                return;
              }
            }
            if (!isEmpty(value)) {
              value = formatService.formatNumber(value, pattern);
              setter($scope, formatService.parseNumber(value));
              $element.val(value);
            }
          }
        });
        watchers.push(watcher);
      } else if (formatType == 'money') {
        watcher = $scope.fsGridConfig.appScope.$watch($attr.fsMoneyFormat, function(pattern) {
          if (needFormat && !isEmpty(pattern)) {
            value = getter($scope);
            if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
              if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
                $element.val('');
                return;
              }
            }
            if (!isEmpty(value)) {
              value = formatService.formatMoney(value, pattern);
              setter($scope, formatService.parseMoney(value));
              $element.val(value);
            }
          }
        });
        watchers.push(watcher);
        watcher = $scope.$watch('ngModel.' + $attr.fsMoneyFormat, function(pattern) {
          if (needFormat && !isEmpty(pattern)) {
            value = getter($scope);
            if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
              if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
                $element.val('');
                return;
              }
            }
            if (!isEmpty(value)) {
              value = formatService.formatMoney(value, pattern);
              setter($scope, formatService.parseMoney(value));
              $element.val(value);
            }
          }
        });
        watchers.push(watcher);
      } else if (formatType == 'sensitive') {
        watcher = $scope.$watch($attr.fsSstFormat, function(pattern) {
          if (needFormat && !isEmpty(pattern)) {
            value = getter($scope);
            if (!isEmpty(value)) {
              value = formatService.formatSst(value, pattern);
              $element.val(value);
            }
          }
        });
        watchers.push(watcher);
      } else if (formatType == 'percentage') {
        watcher = $scope.fsGridConfig.appScope.$watch($attr.fsPctFormat, function(pattern) {
          if (needFormat && !isEmpty(pattern)) {
            value = getter($scope);
            if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
              if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
                $element.val('');
                return;
              }
            }
            if (!isEmpty(value)) {
              value = formatService.formatPercentage(value, pattern);
              setter($scope, formatService.parsePercentage(value));
              $element.val(value);
            }
          }
        });
        watchers.push(watcher);
        watcher = $scope.$watch('ngModel.' + $attr.fsPctFormat, function(pattern) {
          if (needFormat && !isEmpty(pattern)) {
            value = getter($scope);
            if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
              if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
                $element.val('');
                return;
              }
            }
            if (!isEmpty(value)) {
              value = formatService.formatPercentage(value, pattern);
              setter($scope, formatService.parsePercentage(value));
              $element.val(value);
            }
          }
        });
        watchers.push(watcher);
      } else if (formatType == 'date') {
        watcher = $scope.fsGridConfig.appScope.$watch($attr.fsDateFormat, function(pattern) {
          if (needFormat && !isEmpty(pattern)) {
            value = getter($scope);
            if (!isEmpty(value)) {
              value = formatService.formatDate(value, pattern);
              setter($scope, formatService.parseDate(value, pattern));
              $element.val(value);
            }
          }
        });
        watchers.push(watcher);
        watcher = $scope.$watch('ngModel.' + $attr.fsDateFormat, function(pattern) {
          if (needFormat && !isEmpty(pattern)) {
            value = getter($scope);
            if (!isEmpty(value)) {
              value = formatService.formatDate(value, pattern);
              setter($scope, formatService.parseDate(value, pattern));
              $element.val(value);
            }
          }
        });
        watchers.push(watcher);
      }
    } catch (e) {}

    $element.bind('focus', function(e) {
      needFormat = false;
      if (formatType != 'date') {
        if (angular.isNumber(getter($scope)) && getter($scope) == 0 && ngModelCtrl.$viewValue == '') {
          if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
            return;
          }
        }
        // see where the cursor is before the update so that we can set it back
        var selection = getCursortPosition($element[0]);
        $element.val(getter($scope));
        // set back the cursor after rendering
        $element[0].selectionStart = selection;
        $element[0].selectionEnd = selection;
      }
    });

    $element.bind('blur', function(e) {
      needFormat = true;
      value = getter($scope);
      if (formatType == 'number' && !isEmpty(value)) {
        if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
          if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
            return;
          }
        }
        pattern = getPattern(formatType);
        value = formatService.formatNumber(value, pattern);
        if (!isEmpty(value)) {
          setter($scope, formatService.parseNumber(value));
          $element.val(value);
        } else {
          setter($scope, '');
          $element.val('');
        }
      } else if (formatType == 'money' && !isEmpty(value)) {
        if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
          if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
            return;
          }
        }
        pattern = getPattern(formatType);
        value = formatService.formatMoney(value, pattern);
        if (!isEmpty(value)) {
          setter($scope, formatService.parseMoney(value));
          $element.val(value);
        } else {
          setter($scope, '');
          $element.val('');
        }
      } else if (formatType == 'sensitive' && !isEmpty(value)) {
        pattern = getPattern(formatType);
        value = formatService.formatSst(value, pattern);
        if (!isEmpty(value)) {
          $element.val(value);
        } else {
          $element.val('');
        }
      } else if (formatType == 'percentage' && !isEmpty(value)) {
        if (angular.isNumber(value) && value == 0 && ngModelCtrl.$viewValue == '') {
          if ($scope.fsGridConfig.filter && $scope.fsGridConfig.filter == 'zeroEmpty') {
            return;
          }
        }
        pattern = getPattern(formatType);
        value = formatService.formatPercentage(value, pattern);
        if (!isEmpty(value)) {
          setter($scope, formatService.parsePercentage(value));
          $element.val(value);
        } else {
          setter($scope, '');
          $element.val('');
        }
      }
    });
    /*if (formatType != 'date' && formatType != 'sensitive' && formatType != 'number') {
			$element.bind('keydown', function(e) {
				function stopEvent(e) {
					if(e.preventDefault){
			            e.preventDefault();
			        }else{
			            e.returnValue = false;
			        }
				}
				var theEvent = window.event || e;
				var code = theEvent.keyCode || theEvent.which;
				if (getter($scope) && ('' + getter($scope)).indexOf('.') >= 0 && (code == 110 || code == 190)) {
					stopEvent(theEvent);
				}
				if (((code != 8 && code != 37 && code != 39 && code < 48) || (code > 57 && code < 96) || code > 105) && code != 110 && (code != 190 || theEvent.shiftKey == true) && !(code == 86 && theEvent.ctrlKey == true)) {
					stopEvent(theEvent);
				}
			});
		}*/
    if (formatType == 'number' || formatType == 'money' || formatType == 'percentage') {
      $element.bind('keydown', function(e) {
        function stopEvent(e) {
          if (e.preventDefault) {
            e.preventDefault();
          } else {
            e.returnValue = false;
          }
        }
        var theEvent = window.event || e;
        var position = getCursortPosition($element[0]);
        var value = $element.val(),
          isValidKey = true;
        var formatInfo = storageService.getFormatInfo() || {};
        if (formComponentCtrl.fsGridConfig && formComponentCtrl.fsGridConfig.options && formComponentCtrl.fsGridConfig.options.allowNegative) {
          if (keyDownService.minusKeyDown(e) && (isEmpty(value) || value.indexOf('-') == -1)) return;
          if (position == 0 && value.indexOf('-') != -1) {
            stopEvent(theEvent);
            return;
          }
          if (keyDownService.minusKeyDown(e) && position > 0) {
            stopEvent(theEvent);
            return;
          }
        }

        pattern = getPattern(formatType);
        if (formatType == 'money') {
          var ccyConfig = SYS_PARAMS.CCY_CONFIG,
            zeros = '.000000000000000000000000000000000000000000000000',
            fractionDigits;
          formatInfo = storageService.getFormatInfo() || {};
          if (ccyConfig[angular.uppercase(pattern)]) {
            fractionDigits = ccyConfig[angular.uppercase(pattern)];
            pattern = (formatInfo.money0 || '#,##0') + zeros.substr(0, fractionDigits + 1);
          } else {
            pattern = formatInfo[pattern];
            pattern = pattern || formatInfo['defaultMoney'];
          }
        } else if (formatType == 'percentage') {
          pattern = formatInfo[pattern];
          pattern = pattern || defaultFormat.percentage;
        } else if (formatType == 'number') {
          pattern = formatInfo[pattern];
          pattern = pattern || formatInfo.defaultFloat;
        }
        if (pattern.indexOf('.') >= 0) {
          isValidKey = keyDownService.floatKeyDown(e, value);
        } else {
          isValidKey = keyDownService.numKeyDown(e, value);
        }

        if (!isValidKey) {
          stopEvent(theEvent);
        }
      });
    }
  }

  function getCursortPosition(textDom) {
    var cursorPos = 0;
    if (document.selection) {
      // IE Support
      textDom.focus();
      var selectRange = document.selection.createRange();
      selectRange.moveStart('character', -textDom.value.length);
      cursorPos = selectRange.text.length;
    } else if (textDom.selectionStart || textDom.selectionStart == '0') {
      // Firefox support
      cursorPos = textDom.selectionStart;
    }
    return cursorPos;
  }
})(angular);
